﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.Windows;
using SlimDX.D3DCompiler;
using Device = SlimDX.Direct3D11.Device;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;

namespace Vxl
{
    public class VxlEngineSetupHelper
    {
        public static Control vxlEngineControl = null;
        public static Device dxDevice = null;
        public static SwapChain dxSwapChain = null;
        public static RenderTargetView dxRenderView = null;
        public static VxlEngine vxlEngine = null;
        public static Texture2D dxBackBuffer = null;
        public static DepthStencilView dsv;
        private static Stopwatch stopWatch;

        public static void Setup(Control control)
        {
            vxlEngineControl = control;
            
            var desc = new SwapChainDescription()
            {
                BufferCount = 1,
                ModeDescription = new ModeDescription(vxlEngineControl.Width, vxlEngineControl.Height, new Rational(60, 1), Format.R8G8B8A8_UNorm),
                IsWindowed = true,
                OutputHandle = vxlEngineControl.Handle,                
                SampleDescription = new SampleDescription(1, 0),
                SwapEffect = SwapEffect.Discard,
                Usage = Usage.RenderTargetOutput
            };

            Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.Debug, desc, out dxDevice, out dxSwapChain);

            Factory factory = dxSwapChain.GetParent<Factory>();
            factory.SetWindowAssociation(vxlEngineControl.Handle, WindowAssociationFlags.IgnoreAll);

            dxBackBuffer = Texture2D.FromSwapChain<Texture2D>(dxSwapChain, 0);
            dxRenderView = new RenderTargetView(dxDevice, dxBackBuffer);

            dxDevice.ImmediateContext.Rasterizer.SetViewports(new Viewport(0, 0, vxlEngineControl.Width, vxlEngineControl.Height, 0.1f, 1.0f));
            
            SampleDescription sampleDescription = new SampleDescription();
            sampleDescription.Count = 1;
            sampleDescription.Quality = 0;

            Texture2DDescription texdesc = new Texture2DDescription();
            texdesc.Width = vxlEngineControl.Width;
            texdesc.Height = vxlEngineControl.Height;
            texdesc.MipLevels = 1;
            texdesc.ArraySize = 1;
            texdesc.Format = Format.D32_Float;
            texdesc.SampleDescription = sampleDescription;
            texdesc.Usage = ResourceUsage.Default;
            texdesc.BindFlags = BindFlags.DepthStencil;
            texdesc.CpuAccessFlags = CpuAccessFlags.None;
            texdesc.OptionFlags = ResourceOptionFlags.None;


            DepthStencilViewDescription dsvd = new DepthStencilViewDescription();
            dsvd.Format = texdesc.Format;
            dsvd.Dimension = DepthStencilViewDimension.Texture2DMultisampled;
            Texture2D DepthStencil = new Texture2D(dxDevice, texdesc);
            dsv= new DepthStencilView(dxDevice, DepthStencil, dsvd);

            dxDevice.ImmediateContext.OutputMerger.SetTargets(dsv, dxRenderView);
            vxlEngine = new VxlEngine(dxDevice, vxlEngineControl);
            stopWatch = new Stopwatch();
        }

        public static void Resize(Control control){

            /*g_pd3dDevice->OMSetRenderTargets(0, 0, 0);

            // Release all outstanding references to the swap chain's buffers.
            g_pRenderTargetView->Release();

            HRESULT hr;
            hr = g_pSwapChain->ResizeBuffers(BUFFER_COUNT, width, height,
                                            DXGI_FORMAT_R8G8B8A8_UNORM, 0);
            // Perform error handling here!

            // Get buffer and create a render-target-view.
            ID3D10Texture2D* pBuffer;
            hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D),
                                         (void**)&pBuffer);
            // Perform error handling here!

            hr = g_pd3dDevice->CreateRenderTargetView(pBuffer, NULL,
                                                     &g_pRenderTargetView);
            // Perform error handling here!
            pBuffer->Release();

            g_pd3dDevice->OMSetRenderTargets(1, &g_pRenderTargetView, NULL);

            // Set up the viewport.
            D3D10_VIEWPORT vp;
            vp.Width = width;
            vp.Height = height;
            vp.MinDepth = 0.0f;
            vp.MaxDepth = 1.0f;
            vp.TopLeftX = 0;
            vp.TopLeftY = 0;
            g_pd3dDevice->RSSetViewports(1, &vp);
             * */

            //dxDevice.ImmediateContext.OutputMerger.SetTargets(null, null, null);
            
        }

        public static void Run()
        {
            stopWatch.Start();

            MessagePump.Run((RenderForm)vxlEngineControl, () =>
            {
                RunOnce();
            });
            Dispose();
        }

        public static void StartStopWatch(){
            stopWatch.Start();
        }

        public static void RunOnce()
        {
            float dt = stopWatch.ElapsedMilliseconds * 0.001f;

            stopWatch.Restart();
            dxDevice.ImmediateContext.ClearRenderTargetView(dxRenderView, Color.Black);

            dxDevice.ImmediateContext.ClearDepthStencilView(dsv, DepthStencilClearFlags.Depth, 1.0f, 0);

            vxlEngine.Update(dt);
            vxlEngine.Draw(dt);

            dxSwapChain.Present(1, PresentFlags.None);
        }

        public static void Dispose(){

            vxlEngine.CleanUp();

            dxRenderView.Dispose();
            dsv.Dispose();
            dxBackBuffer.Dispose();
            if (!dxDevice.Disposed)
                dxDevice.Dispose();

            dxSwapChain.Dispose();
        }

        public static void SetupAndRun(RenderForm form)
        {
            Setup(form);
            Run();
        }
    }
}
